﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Reflection;

namespace TerenXNA 
{
    public class HeightData 
    {
        // siatka
        public VertexPositionColorNormalTexture[] vertices;
        public int[] indices;

        public float[,] heightData;

        public int verticesLength;
        public int indicesLength; 
        public VertexBuffer myVertexBuffer;
        public IndexBuffer myIndexBuffer;

        private int wx;
        private int wy;
        private int scale;
        private string hfolder;
        private string tfolder;
        // tekstura
        public Texture2D imageTexture;
        public bool textureLoaded = false;
        public bool loadTexture = false; 
        
        public HeightData(bool lt, string f, string t, int x, int y, int s)
        {
            this.loadTexture = lt; 
            this.wx = x;
            this.wy = y;
            this.hfolder = f;
            this.tfolder = t;
            this.scale = s;

            vertices = null;
            verticesLength = 0;
            indices = null;
            indicesLength = 0;
            myVertexBuffer = null;
            myIndexBuffer = null;
        }

        public bool load()
        {
            SetUpVertices();
            SetUpIndices();
            CalculateNormals();
            CopyToBuffers();

            return true;
        }

        //wczytanie mapy wysokosci z pliku
        public bool loadTerrain()
        {
            if (heightData != null) return true;

            Stream stream = null;
            try
            {
                IFormatter formatter = new BinaryFormatter();
                stream = new FileStream(World.path + hfolder, FileMode.Open, FileAccess.Read, FileShare.None);
                heightData = (float[,])formatter.Deserialize(stream);
                /*
                for (int i=0; i<heightData.GetLength(0); i++)
                {
                    for (int j = 0; j < heightData.GetLength(1); j++)
                    {
                        heightData[i, j] *= Game1.heightScale;
                    }
                }
                */
                
               // Console.WriteLine("wczytalem teren: " + World.path + hfolder);

                if (!textureLoaded&&loadTexture)
                {
                    //FileStream titleStream = new FileStream(World.path + "test.bmp", FileMode.Open, FileAccess.Read, FileShare.None);
                    //stream = new FileStream(World.path + tfolder, FileMode.Open, FileAccess.Read, FileShare.None);
                    //FileStream titleStream = File.OpenRead(World.path + "test.bmp");
                    //imageTexture = Texture2D.FromStream(Game1.device, stream);

                    //System.Drawing.Bitmap image = new System.Drawing.Bitmap(World.path + tfolder);
                    //imageTexture = new Texture2D(Game1.device, image.Width, image.Height, false, SurfaceFormat.Color);
                    //Console.WriteLine("wczytalem tx: " + World.path + tfolder);

                    FileStream fs = File.OpenRead(World.path + tfolder);
                    byte[] b = new byte[fs.Length];
                    fs.Read(b, 0, b.Length);
                    fs.Close();
                    
                    MemoryStream ms = new MemoryStream(b);
                    //image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                    ms.Seek(0, SeekOrigin.Begin);
                    imageTexture = Texture2D.FromStream(Game1.device, ms);
                    //imageTexture = Texture2D.FromStream(Game1.device, ms, image.Width, image.Height, false);
                    ms.Close();
                    ms = null;

                    textureLoaded = true;
                }

                return true;
            }
            catch
            {

            }
            finally
            {
                if (null != stream)
                    stream.Close();
            }
            return false;
        }

        //funkcje to generowanie siatki terenu
        public void SetUpVertices()
        {
            vertices = new VertexPositionColorNormalTexture[(wx + 1) * (wy + 1)];
            for (int x = 0; x < wx; x++)
            {
                for (int y = 0; y < wx; y++)
                {
                    vertices[x + (y * (wx))].Position = new Vector3(x * scale, heightData[x, y], y * scale);

                    //Color kolor = TKolor.generujKolor1(heightData[x, y]);
                    //if (World.isHeightMapVisible)
                    //{
                    Color kolor = Color.White;//TKolor.generujKolor2(heightData[x, y]);
                        vertices[x + (y * (wx))].Color = kolor;
                    //}
                    //else
                   //// {
                   //    vertices[x + (y * (wx))].Color = Color.White;
                   // }
                        vertices[x + (y * (wx))].TextureCoordinate.X = (float)x / (float)(wx-1);// (float)1 * (float)0.0337 + (float)x / (((float)wx)) / 30 + (float)0.004;
                        vertices[x + (y * (wx))].TextureCoordinate.Y = (float)y / (float)(wy-1);// (float)1 * (float)0.0331 + (float)y / (((float)wx)) / 30 - (float)0.001;
                }
            }
        }

        public void SetUpIndices()
        {
            indices = new int[((wx - 1)) * ((wy - 1)) * 6];
            int counter = 0;
            for (int y = 0; y < (wy - 1); y++)
            {
                for (int x = 0; x < (wx - 1); x++)
                {
                    int lowerLeft = x + y * (wx);
                    int lowerRight = (x + 1) + y * (wx);
                    int topLeft = x + (y + 1) * (wx);
                    int topRight = (x + 1) + (y + 1) * (wx);

                    indices[counter++] = topLeft;
                    indices[counter++] = lowerRight;
                    indices[counter++] = lowerLeft;

                    indices[counter++] = topLeft;
                    indices[counter++] = topRight;
                    indices[counter++] = lowerRight;
                }
            }
        }

        public void CalculateNormals()
        {
            for (int i = 0; i < vertices.Length; i++)
                vertices[i].Normal = new Vector3(0, 1, 0);

            for (int i = 0; i < indices.Length / 3; i++)
            {
                int index1 = indices[i * 3];
                int index2 = indices[i * 3 + 1];
                int index3 = indices[i * 3 + 2];

                Vector3 side1 = vertices[index1].Position - vertices[index3].Position;
                Vector3 side2 = vertices[index1].Position - vertices[index2].Position;
                Vector3 normal = Vector3.Cross(side1, side2);

                vertices[index1].Normal += normal;
                vertices[index2].Normal += normal;
                vertices[index3].Normal += normal;
            }

            for (int i = 0; i < vertices.Length; i++)
                vertices[i].Normal.Normalize();
        }

        public void CopyToBuffers()
        {
            if (Game1.exiting)
                return;

            if (vertices == null)
                return;

            myVertexBuffer = new VertexBuffer(Game1.device, VertexPositionColorNormalTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
            myVertexBuffer.SetData(vertices);

            if (indices == null)
                return;

            myIndexBuffer = new IndexBuffer(Game1.device, typeof(int), indices.Length, BufferUsage.WriteOnly);
            myIndexBuffer.SetData(indices);

            verticesLength = vertices.Length;
            indicesLength = indices.Length;
            vertices = null;
            indices = null;
        }
    }

    //struktura wierzcholkow
    public struct VertexPositionColorNormalTexture
    {
        public Vector3 Position;
        public Color Color;
        public Vector3 Normal;
        public Vector2 TextureCoordinate;

        public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
        (
            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
            new VertexElement(sizeof(float) * 3, VertexElementFormat.Color, VertexElementUsage.Color, 0),
            new VertexElement(sizeof(float) * 3 + 4, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
            new VertexElement(sizeof(float) * 6 + 4, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
        );
    }
}
